/*
 * This script handles all  communication with the DL proxy.
 * 
 * Author: John Manero
 */

(function() {

	/**
	 * Manage communication with long-poll server. Allow both server and client
	 * to send messages asynchronously with low latency.
	 * 
	 */
	var Communicator = function() {
		/**
		 * Calback for an asynchronous send event
		 * 
		 * function(request {string}) {}
		 */
		this.onsend = function() {
		};

		/**
		 * Callback for a message
		 * 
		 * function(response {string}) {}
		 */
		this.onmessage = function() {
		};

		/**
		 * Callback for a keepalive connection event
		 * 
		 * function(request {string}) {};
		 */
		this.onkeepalive = function() {
		};

		/**
		 * Callabck for a timeout event
		 * 
		 * function(jq {jqXHR}) {};
		 */
		this.ontimeout = function() {
		};

		/**
		 * Callback for a connection error
		 * 
		 * function(jq {jqXHR}, type {string}) {};
		 */
		this.onerror = function() {
		};

		/**
		 * Wait time in milliseconds before next poll request
		 */
		this.hold = 250;

		/**
		 * AJAX request timeout, in milliseconds
		 */
		this.timeout = 30 * 1000;
		
		/**
		 * Wait time in milliseconds before startup
		 */
		this.starthold = 5 * 1000;
	};

	// Private properties
	var runFlag = false;
	var errorCount = 0;
	var jqRequest = {};

	/**
	 * Start the long-poll cycle
	 */
	Communicator.prototype.start = function() {
		var self = this;
		setTimeout(function() {
			start(self);
		}, this.starthold);
	};

	/**
	 * Send a message to the server
	 * 
	 * @param path
	 *            {string} HTTP request path
	 * @param req
	 *            {object} POST Request Body
	 */
	Communicator.prototype.send = function(path, req) {

		var options = {};
		options.contentType = 'application/json';
		options.dataType = 'json';
		options.type = 'POST';

		send(this, path, req, options);
	};
	
	/**
	 * Start the poller
	 * 
	 * @private
	 */
	function start(comm) {
		if (runFlag)
			return;

		runFlag = true;

		var options = {};
		options.contentType = 'application/json';
		options.dataType = 'json';
		options.type = 'POST';

		poll(comm, '/keepalive', options);
	}

	/**
	 * Recursive polling routine
	 * 
	 * @private
	 */
	function poll(comm, url, options) {
		if (runFlag) {
			options = options || {};

			options.url = url;
			options.timeout = comm.timeout;

			options.success = function(res) {
				errorCount = 0;
				comm.onmessage(res);

				if (runFlag) {
					setTimeout(function() {
						poll(comm, url, options);
					}, comm.hold);
				}
			};

			options.error = function(jq, type) {
				switch (type) {
				case 'timeout': // Not an error. Just reconnect.
					comm.ontimeout(jq);

					if (runFlag) {
						setTimeout(function() {
							poll(comm, url, options);
						}, comm.hold);
					}
					break;

				case 'abort':
					break; // Do Nothing

				default:
					errorCount++;
					comm.onerror(jq, type);

					if (errorCount < 5) {
						if (runFlag) {
							setTimeout(function() {
								poll(comm, url, options);
							}, comm.hold);
						}
					}
				}
			};

			comm.onkeepalive();
			jqRequest = jQuery.ajax(options);
		}
	}

	/**
	 * Asynchronous send routine
	 * 
	 * @private
	 */
	function send(comm, url, req, options) {
		options = options || {};
		if (typeof (jqRequest.abort) == 'function') {
			jqRequest.abort();
		}

		comm.onsend(req);
		runFlag = false;

		options.url = url;
		options.data = JSON.stringify(req);
		options.timeout = comm.timeout;

		options.success = function(res) {
			comm.onmessage(res);

			if (!runFlag) {
				setTimeout(function() {
					start(comm);
				}, comm.hold);
			}
		};

		options.error = function(jq, type) {
			switch (type) {
			case 'timeout': // Not an error. Just reconnect.
				comm.ontimeout(jq);

				if (!runFlag) { // Jump back to poll routine
					setTimeout(function() {
						start(comm);
					}, comm.hold);
				}
				break;

			case 'abort':
				break; // Do Nothing

			default:
				errorCount++;
				comm.onerror(jq, type);

				if (errorCount < 5) {
					if (runFlag) {
						setTimeout(function() {
							start(comm);
						}, comm.hold);
					}
				}
			}
		};

		jQuery.ajax(options);
	}

	/**
	 * Generate a keepalive request object
	 * 
	 * @private
	 */
	function keepalive(comm) {
		this.__XMLclass = 'request';
		this.id = uuid.v4();
		this.__XMLchildren = {
			__XMLclass : 'keepalive',
			timeout : comm.timeout
		};
	}

	// Export class to browser
	window.Communicator = Communicator;
}).call();